#2372: Add --retention-delay option to ide cleanup - #2378
krystynaShatkovska wants to merge 8 commits into
Conversation
Adds a --retention-delay option to the cleanup commandlet to delete stale files that have not been modified within a configurable period. Files are scanned recursively under $IDE_HOME/updates, $IDE_ROOT/_ide/tmp and ~/Downloads/ide. The option accepts a time-based ISO-8601 duration (e.g. P30D) and defaults to 1 year (365 days) if not provided. Empty folders left behind after deleting stale files are removed, while the scanned roots themselves are kept.
Coverage Report for CI Build 35575661066Coverage increased (+0.1%) to 74.272%Details
Uncovered ChangesNo uncovered changes found. Coverage Regressions15 previously-covered lines in 2 files lost coverage.
Coverage Stats💛 - Coveralls |
samuelkos17
left a comment
There was a problem hiding this comment.
Thanks for adding the --retention-dely to the cleanup commandlet. I tried to follow your testing steps, however the commands you provided didn't work out for me. I've manually moved the files to /_ide/tmp though and then ran the cleanup command and it worked!
While reviewing I found some problems that could lead to issues and that really need to get addressed before moving this to In Review. You can find them in the Comments here.
Besides that I still have on recommendation:
documentation/tmp.adoc line 18 needs to be updated according to the new functionality.
…le stale) - Skip symbolic links during the stale-file scan to avoid following links outside the scanned roots or recursing into self-referencing links - Rename retentionDelay field to retentionDelayOption to avoid clashing with the local Duration variable - Guard only the updates root on a missing IDE_HOME; the temp and download roots are user-level and always available - Add test coverage and move the changelog entry to release 2026.09.002
There was a problem hiding this comment.
Thanks for resolving my comments! I've tested the changes but came across another issue:
ide cleanup now wants to delete all my IDEasy installations including the one i'm currently using, the --retention-delay can have any value for that to happen, so i guess it doesn't work there. Does this problem also occur on ur end?
ide cleanup marks every installed software version that no project links to as "unused" and deletes it. Global tools (including IDEasy itself) install directly into the software repository and are not linked by any project, so the currently running IDEasy installation was being deleted as well. Resolve the running installation via the _ide/installation link and never mark that version for deletion. Add a test that verifies the running version is kept while genuinely unused versions are still removed, plus a junction-based directory-link helper so the test runs on Windows without elevated privileges.
There was a problem hiding this comment.
Thanks for implementing a guard to keep the current IDEasy installation from deleting, however while looking at your new changes I realized that the retention-delay has no impact on installed tool versions (claude, intellij, uv, ideasy). Even when you run ide cleanup with retention-delay set to any positive value the command wants to delete all installed tool versions that no project links to. I'm not quite sure if this intentional, but judging by what the issue description says I think it's not intentional and that you need to add the retention-delay checks there aswell, but please correct me if I'm wrong.
This PR fixes #2372
Implemented changes:
Adds a --retention-delay option to ide cleanup. It takes an ISO-8601 duration (e.g. P30D, PT2H30M; default 1 year) and deletes stale files (not modified within that period) in $IDE_HOME/updates, $IDE_ROOT/_ide/tmp and ~/Downloads/ide. After deletion it deletes empty folders (but keeps the scanned roots). Invalid durations are rejected with a clear error.
Testing instructions
Automated tests
Run the test class, it covers all the core cases:
mvn -pl cli test -Dtest=CleanupCommandletTest
Manual test
ide cleanup scans three roots for stale files: $IDE_HOME/updates (only inside a project), $IDE_ROOT/_ide/tmp, and the download cache ~/Downloads/ide. The manual test must be run against a throwaway IDE root, not your real one, for two reasons:
So you must isolate both the IDE root and the user home, and run from a working directory outside your IDE tree. Using exec-ide.sh / mvn exec:exec does not achieve this (the forked process's working directory is the project), so the steps below invoke java directly.
Set the throwaway location once (any path outside your IDE tree, Windows example shown):
$THROW = a fresh temp directory of your choice, e.g. %TEMP%\ide-cleanup-manual
$RUN = a sibling empty directory, e.g. %TEMP%\ide-cleanup-run
mvn -pl cli dependency:build-classpath -Dmdep.outputFile=cli/cp.txt
Windows (PowerShell - uses a directory junction, no admin needed):
$T = "C:\Users<you>\AppData\Local\Temp\ide-cleanup-manual" # your $THROW
if (Test-Path $T) { Remove-Item $T -Recurse -Force }
New-Item -ItemType Directory -Path "$T_ide\tmp" -Force | Out-Null
New-Item -ItemType Directory -Path "$T_ide\software\maven\ideasy\default\2026.08.002" -Force |
Out-Null # running install
New-Item -ItemType Directory -Path "$T_ide\software\maven\ideasy\default\1.0" -Force | Out-Null
# unused install
New-Item -ItemType Directory -Path "$T\Downloads\ide" -Force | Out-Null
Set-Content "$T_ide\tmp\stale.bin" "x" -Force
Set-Content "$T_ide\tmp\fresh.bin" "x" -Force
(Get-Item "$T_ide\tmp\stale.bin").LastWriteTime = (Get-Date).AddDays(-31)
New-Item -ItemType Junction -Path "$T_ide\installation" -Target
"$T_ide\software\maven\ideasy\default\2026.08.002" | Out-Null
New-Item -ItemType Directory -Path "$env:TEMP\ide-cleanup-run" -Force | Out-Null # empty cwd,
outside the IDE tree
macOS / Linux (bash — uses a symlink):
T=$(mktemp -d) # your $THROW
mkdir -p "$T/_ide/tmp" "$T/_ide/software/maven/ideasy/default/2026.08.002"
"$T/_ide/software/maven/ideasy/default/1.0" "$T/Downloads/ide"
echo x > "$T/_ide/tmp/stale.bin"; echo x > "$T/_ide/tmp/fresh.bin"
touch -d "31 days ago" "$T/_ide/tmp/stale.bin" # (GNU) # macOS: touch -t
ln -s "$T/_ide/software/maven/ideasy/default/2026.08.002" "$T/_ide/installation"
mkdir -p "$TMPDIR/ide-cleanup-run"
Windows (Git Bash):
T='C:\Users<you>\AppData\Local\Temp\ide-cleanup-manual'
cp_file='/cli/cp.txt'
classes='/cli/target/classes'
cd "$env:TEMP/ide-cleanup-run" # empty dir, outside the IDE tree
export IDE_ROOT="$T"
CP="$classes;$(tr -d '\n' < "$cp_file")"
printf 'yes\nyes\n' | java -Duser.home="$T" -cp "$CP" com.devonfw.tools.ide.cli.Ideasy cleanup
--retention-delay=P30D
macOS / Linux:
classes="$(pwd)/cli/target/classes"; cp_file="$(pwd)/cli/cp.txt"
cd "$TMPDIR/ide-cleanup-run" # empty dir, outside the IDE tree
export IDE_ROOT="$T"
CP="$classes:$(tr -d '\n' < "$cp_file")" # note: ':' separator on unix
printf 'yes\nyes\n' | java -Duser.home="$T" -cp "$CP" com.devonfw.tools.ide.cli.Ideasy cleanup
--retention-delay=P30D
Before it deletes anything, confirm isolation: the IDE prints its resolved document path and IDE_ROOT is set to .... Both must point at your $THROW, not your real IDE root. If it prints your real root, stop - the isolation failed and it will act on your real installation.
$THROW/_ide/tmp/stale.bin → deleted (stale)
$THROW/_ide/tmp/fresh.bin → kept (fresh)
$THROW/_ide/software/maven/ideasy/default/1.0 → deleted (unused)
$THROW/_ide/software/maven/ideasy/default/2026.08.002 → kept (running install, guarded)
Windows:
Test-Path "$T_ide\tmp\stale.bin" # expected: False
Test-Path "$T_ide\tmp\fresh.bin" # expected: True
Test-Path "$T_ide\software\maven\ideasy\default\1.0" # expected: False
Test-Path "$T_ide\software\maven\ideasy\default\2026.08.002" # expected: True
macOS / Linux:
test -e "$T/_ide/tmp/stale.bin"; echo $? # expected 1 (False)
test -e "$T/_ide/tmp/fresh.bin"; echo $? # expected 0 (True)
test -e "$T/_ide/software/maven/ideasy/default/1.0"; echo $? # expected 1 (False)
test -e "$T/_ide/software/maven/ideasy/default/2026.08.002"; echo $? # expected 0 (True)
PT6M10D # months not allowed in a time-based duration
P0D # must be positive
-P30D # must be positive
abc # not ISO-8601
(Use the same java -Duser.home="$T" -cp "$CP" ... invocation as step 3, with each value substituted for P30D.)
rm -rf <your $THROW>
Checklist for this PR
Make sure everything is checked before merging this PR. For further info please also see
our DoD.
mvn clean testlocally all tests pass and build is successful#«issue-id»: «brief summary»(e.g.#921: fixed setup.batand notfeature/921 fixed setup.bat). If no issue ID exists, title only.In Progressand assigned to you or there is no issue (might happen for very small PRs)with
internalpom.xmlfiles or otherwise if runtime dependencies changed, you have updated our LICENSE.asciidoc